home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2007 December
/
PCWKCD1207B.iso
/
Blogowanie poza sfera
/
Flock 1.0 beta
/
flock-1.0RC3.en-US.win32.exe
/
flock
/
components
/
flockOpmlService.js
< prev
next >
Wrap
Text File
|
2007-10-18
|
15KB
|
528 lines
// vim: tabstop=2 softtabstop=2 shiftwidth=2 expandtab
//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2007
// http://flock.com
//
// This file may be used under the terms of of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// END FLOCK GPL
//
const OPML_CONTRACTID = '@flock.com/opml-service;1';
const OPML_CLASSID = Components.ID('{a3948322-0bb8-413c-8c6d-b0959d6d5bad}');
const OPML_CLASSNAME = 'Flock OPML Service';
const MARK_READ_CONTRACTID = '@flock.com/opml-subscribe-listener-mark-read;1';
const MARK_READ_CLASSID = Components.ID('{4cb09bdd-cca3-4f8d-86b4-d4b315c42b45}');
const MARK_READ_CLASSNAME = 'Flock OPML Mark Read on Subscribe Listener';
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n'
const MAX_OPML_DEPTH = 20;
/* from nspr's prio.h */
const PR_RDONLY = 0x01;
const PR_WRONLY = 0x02;
const PR_RDWR = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND = 0x10;
const PR_TRUNCATE = 0x20;
const PR_SYNC = 0x40;
const PR_EXCL = 0x80;
function writeXML(xml, file) {
var xmlStr = XML_HEADER + xml.toXMLString();
if (file.exists())
file.remove(false);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
var ostream = Cc['@mozilla.org/network/file-output-stream;1']
.createInstance(Ci.nsIFileOutputStream);
ostream.init(file, PR_WRONLY | PR_CREATE_FILE, 0644, 0);
var converter = Cc['@mozilla.org/intl/scriptableunicodeconverter']
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = 'UTF-8';
var convdata = converter.ConvertFromUnicode(xmlStr) + converter.Finish();
ostream.write(convdata, convdata.length);
ostream.flush();
ostream.close();
}
function OpmlRequest(url, folder, flatRoot, listener, ctxt, logger) {
this._url = url;
this._folder = folder;
this._flatRoot = flatRoot;
this._listener = listener;
this._ctxt = ctxt;
this._logger = logger;
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
this._channel = ios.newChannelFromURI(url, null, null);
}
OpmlRequest.prototype = {
get: function OR_get() {
this._channel.asyncOpen(this, null);
},
onStartRequest: function OR_onStartRequest(request, context) {
this._xml = '';
},
onStopRequest: function OR_onStopRequest(request, context, status) {
if (Components.isSuccessCode(status))
this._subscribe();
else if (this._listener)
this._listener.onLoadError(this._ctxt, null);
},
onDataAvailable: function OR_onDataAvailable(request, context, inputStream,
sourceOffset, count) {
var sstream = Cc['@mozilla.org/scriptableinputstream;1']
.createInstance(Ci.nsIScriptableInputStream);
sstream.init(inputStream);
this._xml += sstream.read(count);
},
_sendLoadError: function OR__sendLoadError(errorCode) {
if (this._listener) {
var error = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
error.errorCode = errorCode;
this._listener.onLoadError(this._ctxt, error);
}
},
_subscribe: function OR__subscribe() {
var url = this._url.spec;
this._logger.info('got OPML data from ' + url);
try {
var xmlData = new XML(this._xml.replace(/<\?xml[^?]*\?>/, ''));
}
catch (e) {
this._logger.error('Invalid XML at ' + url);
this._sendLoadError(Ci.flockIError.OPML_INVALID_XML);
return;
}
var body = xmlData.body;
if (xmlData.name() != 'opml' || !body) {
this._logger.error('No OPML data at ' + url);
this._sendLoadError(Ci.flockIError.OPML_NO_DATA);
return;
}
var root;
if (!this._flatRoot) {
var title = xmlData.head.title;
if (title && title.toString().length > 0)
root = [xmlData.head.title.toString()];
else
root = ['Imported Feeds'];
} else {
root = [this._folder];
}
this._feeds = [];
this._tooDeep = false;
this._getFeeds(root, body);
if (this._tooDeep) {
this._logger.error('Hierarchy at ' + url + ' is too deep');
this._sendLoadError(Ci.flockIError.OPML_TOO_DEEP);
return;
}
if (this._feeds.length == 0) {
this._logger.error('No feeds in OPML at ' + url);
this._sendLoadError(Ci.flockIError.OPML_NO_FEEDS);
return;
}
this._logger.info(this._feeds.length + ' feeds found in ' + url);
this._subscribeFeeds();
},
_getFeeds: function OR__getFeeds(hierarchy, node) {
for each (var outline in node.outline) {
var xmlUrl = outline.@xmlUrl;
if (xmlUrl && xmlUrl.toString().length > 0 &&
!outline.hasComplexContent()) {
var feed = { hierarchy: hierarchy,
url: xmlUrl.toString(),
title: null
};
text = outline.@text;
if (!text || text.toString().length == 0)
text = outline.@title;
if (text && text.toString().length > 0)
feed.title = text.toString();
this._logger.info('Found feed ' + feed.url);
this._feeds.push(feed);
} else {
var title = outline.@title;
if (!title || title.toString().length == 0)
title = outline.@text;
if (title && title.toString().length > 0) {
var newHierarchy = hierarchy.concat(title.toString());
if (newHierarchy.length > MAX_OPML_DEPTH) {
this._tooDeep = true;
return;
}
this._logger.info('Found folder ' + title.toString());
if (outline.hasComplexContent()) {
this._getFeeds(newHierarchy, outline);
} else {
var feed = { hierarchy: newHierarchy,
url: null
};
this._feeds.push(feed);
}
if (this._tooDeep)
return;
}
}
}
},
_subscribeFeeds: function OR__subscribeFeeds(folder, node) {
var fm = Cc['@flock.com/feed-manager;1']
.getService(Ci.flockIFeedManager);
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
var subscriber = {
_feeds: this._feeds,
_listener: this._listener,
_ctxt: this._ctxt,
_logger: this._logger,
_url: this._url,
_index: -1,
get _feed() { return this._feeds[this._index]; },
createFolderHierarchy: function(hierarchy) {
var destFolder = null;
for each (var folder in hierarchy) {
if (folder instanceof Ci.flockIFeedFolder) {
destFolder = folder;
} else {
var newFolder;
try {
newFolder = destFolder.addFolder(folder);
this._logger.info('Created folder: ' + folder);
}
catch (e) {
newFolder = destFolder.getChildFolder(folder);
}
destFolder = newFolder;
}
}
return destFolder;
},
onGetFeedComplete: function(feed) {
if (this._listener)
this._listener.onGetFeed(feed, this._ctxt);
var destFolder = this.createFolderHierarchy(this._feed.hierarchy);
this._feed.hierarchy.length = 0;
this._feed.hierarchy.push(destFolder);
this._logger.info('Subscribing ' + this._feed.url + ' to folder ' +
destFolder.getTitle());
destFolder.subscribeFeed(feed);
Cc["@flock.com/metrics-service;1"]
.getService(Ci.flockIMetricsService)
.report("FeedsSidebar-AddFeed", "OpmlImport");
if (this._listener)
this._listener.onSubscribe(feed, this._ctxt,
this._index, this._feeds.length);
this.processNextFeed();
},
onError: function(error) {
this._logger.warn('Error fetching ' + this._feed.url);
if (this._listener)
this._listener.onError(this._feed.url, this._ctxt, error,
this._index, this._feeds.length);
this.processNextFeed();
},
processNextFeed: function() {
this._index++;
while (this._index < this._feeds.length) {
if (this._feed.url == null) {
this.createFolderHierarchy(this._feed.hierarchy);
this._index++;
continue;
}
var url = null;
try {
url = ios.newURI(this._feed.url, null, null);
}
catch (e) { }
if (url) {
this._logger.info('Fetching feed ' + this._feed.url);
fm.getFeed(url, this);
return;
}
if (this._listener) {
this._logger.warn('Bad feed URL: ' + this._feed.url);
var error = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
error.errorCode = error.OPML_BAD_FEED_URL;
this._listener.onError(this._feed.url, this._ctxt, error,
this._index, this._feeds.length);
}
this._index++;
}
this._logger.info('Finished OPML subscription of ' + this._url.spec);
if (this._listener)
this._listener.onFinish(this._ctxt);
}
}
subscriber.processNextFeed();
},
QueryInterface: function OR_QueryInterface(iid) {
if (iid.equals(Ci.nsIStreamListener) ||
iid.equals(Ci.nsIRequestObserver)||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
}
function OpmlService() {
this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
this._logger.init('opmlservice');
this._logger.info('created');
}
OpmlService.prototype = {
subscribe: function OPML_subscribe(url, folder, flatRoot, listener, ctxt) {
this._logger.info('subscribing OPML from ' + url);
var req = new OpmlRequest(url, folder, flatRoot, listener, ctxt, this._logger);
req.get();
},
export: function OPML_export(folder, title, file) {
var opml =
<opml version="1.1">
<head>
<title>{title}</title>
</head>
<body/>
</opml>;
this._exportFolder(folder, opml.body);
writeXML(opml, file);
},
_exportFolder: function OPML__exportFolder(folder, parentNode) {
var children = folder.getChildren();
while (children && children.hasMoreElements()) {
var child = children.getNext();
var node = <outline/>;
if (child instanceof Ci.flockIFeedFolder) {
node.@text = child.getTitle();
this._exportFolder(child, node);
} else {
node.@type = 'rss';
node.@text = child.getTitle();
node.@title = child.getTitle();
node.@xmlUrl = child.getURL().spec;
}
parentNode.outline += node;
}
},
getInterfaces: function OPML_getInterfaces(countRef) {
var interfaces = [Ci.flockIOpmlService, Ci.nsIClassInfo, Ci.nsISupports];
countRef.value = interfaces.length;
return interfaces;
},
getHelperForLanguage: function OPML_getHelperForLanguage(language) {
return null;
},
contractID: OPML_CONTRACTID,
classDescription: OPML_CLASSNAME,
classID: OPML_CLASSID,
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
flags: Ci.nsIClassInfo.SINGLETON,
QueryInterface: function OPML_QueryInterface(iid) {
if (iid.equals(Ci.flockIOpmlService) ||
iid.equals(Ci.nsIClassInfo) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
function MarkReadListener() {
}
MarkReadListener.prototype = {
onSubscribe: function MARK_READ_onSubscribe(feed, ctxt, progress, progressMax) {
},
onFinish: function MARK_READ_onFinish(ctxt) {
},
onLoadError: function MARK_READ_onLoadError(ctxt, error) {
},
onError: function MARK_READ_onError(url, ctxt, error, progress, progressMax) {
},
onGetFeed: function MARK_READ_onGetFeed(feed, ctxt) {
feed.markRead();
},
getInterfaces: function MARK_READ_getInterfaces(countRef) {
var interfaces = [Ci.flockIOpmlSubscribeListener, Ci.nsIClassInfo,
Ci.nsISupports];
countRef.value = interfaces.length;
return interfaces;
},
getHelperForLanguage: function MARK_READ_getHelperForLanguage(language) {
return null;
},
contractID: MARK_READ_CONTRACTID,
classDescription: MARK_READ_CLASSNAME,
classID: MARK_READ_CLASSID,
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
flags: Ci.nsIClassInfo.SINGLETON,
QueryInterface: function MARK_READ_QueryInterface(iid) {
if (iid.equals(Ci.flockIOpmlSubscribeListener) ||
iid.equals(Ci.nsIClassInfo) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
function GenericComponentFactory(ctor) {
this._ctor = ctor;
}
GenericComponentFactory.prototype = {
_ctor: null,
// nsIFactory
createInstance: function(outer, iid) {
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return (new this._ctor()).QueryInterface(iid);
},
// nsISupports
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIFactory) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
};
var Module = {
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIModule) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
getClassObject: function(cm, cid, iid) {
if (!iid.equals(Ci.nsIFactory))
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
if (cid.equals(OPML_CLASSID))
return new GenericComponentFactory(OpmlService)
if (cid.equals(MARK_READ_CLASSID))
return new GenericComponentFactory(MarkReadListener)
throw Cr.NS_ERROR_NO_INTERFACE;
},
registerSelf: function(cm, file, location, type) {
var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
cr.registerFactoryLocation(OPML_CLASSID, OPML_CLASSNAME,
OPML_CONTRACTID,
file, location, type);
cr.registerFactoryLocation(MARK_READ_CLASSID, MARK_READ_CLASSNAME,
MARK_READ_CONTRACTID,
file, location, type);
},
unregisterSelf: function(cm, location, type) {
var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
cr.unregisterFactoryLocation(OPML_CLASSID, location);
cr.unregisterFactoryLocation(MARK_READ_CLASSID, location);
},
canUnload: function(cm) {
return true;
},
};
function NSGetModule(compMgr, fileSpec)
{
return Module;
}